package org.glorylight.hacks.social.controller;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import javax.imageio.ImageIO;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.glorylight.hacks.social.data.Asset;
import org.glorylight.hacks.social.data.InvitationData;
import org.glorylight.hacks.social.data.MailMe;
import org.glorylight.hacks.social.data.MessageData;
import org.glorylight.hacks.social.data.MessageThread;
import org.glorylight.hacks.social.data.Messages;
import org.glorylight.hacks.social.data.PictureData;
import org.glorylight.hacks.social.data.ScrapBookData;
import org.glorylight.hacks.social.data.TimeLine;
import org.glorylight.hacks.social.data.TimeLineDetail;
import org.glorylight.hacks.social.data.TimeLineWrapper;
import org.glorylight.hacks.social.data.TipsData;
import org.glorylight.hacks.social.data.TipsWrapper;
import org.glorylight.hacks.social.data.UserData;
import org.glorylight.hacks.social.data.UserNetworkData;
import org.glorylight.hacks.social.data.UserProfile;
import org.glorylight.hacks.social.service.InvitationService;
import org.glorylight.hacks.social.service.MailService;
import org.glorylight.hacks.social.service.MessageService;
import org.glorylight.hacks.social.service.NotificationService;
import org.glorylight.hacks.social.service.PictureService;
import org.glorylight.hacks.social.service.ScrapBookService;
import org.glorylight.hacks.social.service.TipsService;
import org.glorylight.hacks.social.service.UserNetworkService;
import org.glorylight.hacks.social.service.UserService;
import org.glorylight.hacks.social.util.ModelHelper;
import org.glorylight.hacks.social.util.UUIDHelper;
import org.glorylight.hacks.social.xml.Galeries;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.sun.jersey.api.representation.Form;
import com.sun.jersey.core.util.Base64;
import com.sun.jersey.multipart.BodyPartEntity;
import com.sun.jersey.multipart.MultiPart;

@Deprecated
@Component
@Scope("prototype")
@Path("/manager")
public class ApplicationController {

	private final static Logger log = LoggerFactory.getLogger(ApplicationController.class);
	private final static String BABY_HOME = "www.babytivities.com";
	
	@Context
	UriInfo uriInfo;
	
	@Autowired
	private PictureService picService;
	
	@Autowired
	private ScrapBookService scrapService;
	
	@Autowired
	private NotificationService notificationService;
	
	@Autowired
	private UserService userService;
	
	@Autowired
	private UserNetworkService userNetworkService;
	
	@Autowired
	private UserProfile userProfile;
	
	@Autowired
	private MessageService messageService;
	
	@Autowired
	private MailService mailService;
	
	@Autowired
	private InvitationService invitationService;
	
	@Autowired
	private TipsService tipsService;
	
	
	@POST
	@Consumes(MediaType.MULTIPART_FORM_DATA)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/picture")
	public PictureData consumePicture(MultiPart multiPart) {
		UUID id = UUID.randomUUID();
		String userId = userProfile.getUserCredential().getUsername();
		BodyPartEntity model = (BodyPartEntity) multiPart.getBodyParts().get(0).getEntity();
		try {
			BufferedImage bi = ImageIO.read(model.getInputStream());

			File file = new File("/var/uploaded/"+userId+"/gallery/" + UUIDHelper.uuidToString(id) + ".png");
			String picPath = "services/manager/picture/gallery/"+UUIDHelper.uuidToString(id);
			if (file.isDirectory()) {
				ImageIO.write(bi, "png", file);
			} else {
				file.mkdirs();
				ImageIO.write(bi, "png", file);
			}
			PictureData pictureData = new PictureData(id, picPath, userId, "", "", new Date());
			picService.addPicture(pictureData);
			return pictureData;
		} catch (Exception e) {
			log.error("can not upload picture, {}",e.getMessage(), e);
			return null;
		}
	}

	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("pictures")
	public Galeries getGaleriesForUser() {
		User securityUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		log.info("username for lookup : {}", securityUser.getUsername());
		return populateGalery(picService.findAllByUser(securityUser.getUsername()));
	}

	private Galeries populateGalery(List<PictureData> result) {
		Galeries galeries = new Galeries();
		for (PictureData pd : result)
			galeries.getGaleries().add(pd);
		
		return galeries;
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Path("/followUser")
	public Response followUser(@FormParam("username") String username) {
		try {
				UserData userData = userService.find(userProfile.getUserCredential().getUsername());
				if (userData != null) {
				UserNetworkData data = new UserNetworkData(UUID.randomUUID(), ModelHelper.convertUserData(userData), username, new Date());
				userNetworkService.addNetwork(data);
				
				return Response.status(Response.Status.OK)
						.entity("user followed.")
						.type(MediaType.TEXT_PLAIN).build();
			}
		} catch (Exception e) {
			log.error("error ocur when connecting user ..", e);
		}
		return Response.status(Response.Status.BAD_REQUEST)
				.type(MediaType.TEXT_PLAIN).build();
	}
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/userProfile")
	public UserData userProfile(){
		try {
			UserData userData = userService.find(userProfile.getUserCredential().getUsername());
			return userData;
		} catch (Exception e) {
			log.error("error ocur when connecting user ..", e);
		}
		return new UserData();
	}
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/friendProfile/{id}")
	public UserData friendProfile(@PathParam("id") String id){
		try {
			UserData userData = userService.find(id);
			List<UserNetworkData> networks = userNetworkService.findFollower(id);
			userData.setNetworks(networks);
			
			return userData;
		} catch (Exception e) {
			log.error("error ocur when connecting user ..", e);
		}
		return new UserData();
	}

	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Path("/scrapPage")
	public Response consumeScrap(InputStream image) {
		UUID id = UUID.randomUUID();
		String userId = userProfile.getUserCredential().getUsername();
		boolean isProcessed = false;
		ScrapBookData bookData = null;
		try {
			StringWriter writer = new StringWriter();
			IOUtils.copy(image, writer);
			String data = writer.toString();
			String find = "base64%2C";
			String tokens = data.substring(data.indexOf(find) + find.length());
			String removeSpecialCharacter2F = StringUtils.replace(tokens, "%2F", "/");
			String removeSpecialCharacter2B = StringUtils.replace(removeSpecialCharacter2F, "%2B", "+");
			String removeSpecialCharacter3D = StringUtils.replace(removeSpecialCharacter2B, "%3D", "=");
			
			BufferedImage bi = ImageIO.read(new ByteArrayInputStream(Base64.decode(removeSpecialCharacter3D.getBytes())));
			if(bi == null)
				return Response.status(Response.Status.BAD_REQUEST)
						.entity("Failed to process attachments. Reason : " + "bi is null")
						.type(MediaType.TEXT_PLAIN).build();
			
			File file = new File("/var/uploaded/"+userId+"/scrap/" + UUIDHelper.uuidToString(id) + ".png");
			String picPath = "services/manager/picture/scrap/"+UUIDHelper.uuidToString(id);
			if (file.isDirectory()) {
				ImageIO.write(bi, "png", file);
			} else {
				file.mkdirs();
				ImageIO.write(bi, "png", file);
			}
			isProcessed = true;
			
			bookData = new ScrapBookData(UUIDHelper.uuidToString(id), userId, new Date(), picPath, "", "", true);
			scrapService.addBook(bookData);
			
			log.debug("picture has been process");
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		if (isProcessed) {
			return Response.status(Response.Status.OK)
					.entity(bookData)
					.type(MediaType.APPLICATION_JSON).build();
		}

		return Response.status(Response.Status.BAD_REQUEST)
				.entity(bookData)
				.type(MediaType.APPLICATION_JSON).build();
	}
	
//	@POST
//	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@GET
	@Path("login")
	public Response putLoginToApplication() {
//		if(SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
//			// TODO : consider caching some information on dodo side like baby tips or timeline or something else.
//			Response.status(Response.Status.OK).build();
//		}
		return Response.status(Response.Status.OK).build();
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Path("signup")
	public Response putSignupToApplication(@FormParam("username") String username, @FormParam("email") String email,
			@FormParam("password") String password, @FormParam("month") String month, @FormParam("day") String day, 
			@FormParam("year") String year,@FormParam("sex") String gender ) {
		try {
			SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
			Date birthDate = df.parse(day+"/"+month+"/"+year);
			log.info("try to request with user {} , password {}, email {}, birthdate {}, gender {}", new Object[]{username,password, email, birthDate, gender});
//			UserData ud = new UserData(email, password, username, birthDate, gender, "");
			UserData ud = null;
			userService.addUser(ud);
			
			//TODO call #confirmInvitation ifconfirmId not null
			
			return Response.seeOther(new URI("../login.html")).build();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return Response.status(Response.Status.FORBIDDEN).type(MediaType.TEXT_PLAIN).build();
	}
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/timeline")
	public TimeLineWrapper getTimeLine() {
		TimeLine timeLine = new TimeLine();
		SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy,MM,dd");
		timeLine.setHeadline("test");
		timeLine.setType("default");
		timeLine.setText("hello world");
		Asset asset = new Asset();
		asset.setMedia("resources/preparationmage/jocelynn1.png");
		timeLine.setAsset(asset);
		List<ScrapBookData> scrapBookDatas = scrapService.findAllByUser(userProfile.getUserCredential().getUsername());
		List<TimeLineDetail> jsonDateField = new ArrayList<TimeLineDetail>();
		for(ScrapBookData scrapBookData : scrapBookDatas){
			TimeLineDetail timeLineDetail = new TimeLineDetail();
			timeLineDetail.setStartDate(dateFormatter.format(scrapBookData.getDateCreated()));
			timeLineDetail.setEndDate(dateFormatter.format(scrapBookData.getDateCreated()));
			timeLineDetail.setHeadline(scrapBookData.getTitle());
			timeLineDetail.setText(scrapBookData.getDesc());
			Asset detailAsset = new Asset();
			detailAsset.setMedia(scrapBookData.getPicPath());
			timeLineDetail.setAsset(detailAsset);
			jsonDateField.add(timeLineDetail);
		}
		timeLine.setDate(jsonDateField);
		
		List<TimeLineDetail> eraField = new ArrayList<TimeLineDetail>();
		TimeLineDetail eraDetail = new TimeLineDetail();
		eraDetail.setStartDate(dateFormatter.format(new Date()));
		eraDetail.setEndDate(dateFormatter.format(new Date()));
		timeLine.setEra(eraField);
		
		List<TimeLineDetail> chartField = new ArrayList<TimeLineDetail>();
		TimeLineDetail chartDetail = new TimeLineDetail();
		chartDetail.setStartDate(dateFormatter.format(new Date()));
		chartDetail.setEndDate(dateFormatter.format(new Date()));
		timeLine.setChart(chartField);
		return new TimeLineWrapper(timeLine);
	}
	
	@GET
	@Produces("image/png")
	@Path("picture/gallery/{pictureName}")
	public Response produceGalleryPicture(@PathParam("pictureName") String pictureName) {
		log.info("pictureName : {}", new Object[]{pictureName});
		File file = new File("/var/uploaded/"+userProfile.getUserCredential().getUsername()+"/gallery/"+pictureName + ".png");
		ResponseBuilder response = Response.ok((Object) file);
		response.header("Content-Disposition", "attachment; filename=image_from_server.png");
		return response.build();
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("picture/gallery/{pictureName}")
	public PictureData updatePictureTitle(@PathParam("pictureName") String pictureName, 
			@FormParam("title") String title, @FormParam("description") String description) {
		log.info("pictureName : {}", new Object[]{pictureName});
		UUID pictureId = UUIDHelper.stringToUUID(pictureName.toUpperCase().replace("-", ""));
		PictureData data = null;
		try {
			data = picService.updatePicture(new PictureData(pictureId, null, userProfile.getUserCredential().getUsername(), title, description, null));
		} catch (Exception e) {
			log.error("could not update gallery",e);
		}
		return data;
	}
	
	@GET
	@Produces("image/png")
	@Path("picture/scrap/{pictureName}")
	public Response productPicture(@PathParam("pictureName") String pictureName) {
		log.info("pictureName : {}", new Object[]{pictureName});
		File file = new File("/var/uploaded/"+userProfile.getUserCredential().getUsername()+"/scrap/"+pictureName + ".png");
		ResponseBuilder response = Response.ok((Object) file);
		response.header("Content-Disposition", "attachment; filename=image_from_server.png");
		return response.build();
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("picture/scrap/{pictureName}")
	public ScrapBookData updateScrapTitle(@PathParam("pictureName") String pictureName, 
			@FormParam("title") String title, @FormParam("description") String description) {
		log.info("scrapname : {}", new Object[]{pictureName});
		UUID pictureId = UUIDHelper.stringToUUID(pictureName.toUpperCase().replace("-", ""));
		ScrapBookData data = null;
		try {
			data = scrapService.updateBook(new ScrapBookData(UUIDHelper.uuidToString(pictureId), userProfile.getUserCredential().getUsername(), null, null, title, description, false));
		} catch (Exception e) {
			log.error("could not update scrap",e);
		}
		return data;
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Path("/message")
	public Response sendMesssage(Form data) {
		String senderId = userProfile.getUserCredential().getUsername();
		URI messagePage = null;
		try {
			messagePage = new URI("../message.html");
		} catch (Exception e) {
			log.error("message page is not valid uri");
			Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
		}
		MessageData messageData = new MessageData(UUID.randomUUID(), senderId, 
				 data.getFirst("recipientId", String.class), data.getFirst("subject", String.class), 
				new Date(), data.getFirst("content", String.class), true);
		try {
			messageService.addMessage(messageData);
		} catch (Exception e){
			log.error("error persisting message", e);
			Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
		}
		return Response.seeOther(messagePage).build();
	}
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/messages")
	public Messages getMesssages() {
		List<MessageThread> threads = constructMessages(userProfile.getUserCredential().getUsername());
		List<MessageThread> destination = new ArrayList<MessageThread>(threads.size());
		for(MessageThread messageThread : threads)
			destination.add(SerializationUtils.clone(messageThread));
		for(MessageThread t : destination)
			t.clearMessages();
		
		return new Messages(destination);
	}
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/messages/{messageId}")
	public Messages getMesssagesThread(@PathParam("messageId") String messageId) {
		List<MessageThread> threads = new ArrayList<MessageThread>();
		threads.add(userProfile.getThreadMessage(messageId));
		
		return new Messages(threads);
	}

	private List<MessageThread> constructMessages(String userName) {
		Map<String, List<MessageData>> datas = messageService.findAllByUserPerThread(userName);
		List<MessageThread> threads = new ArrayList<MessageThread>();
		MessageThread mt = null;
		for (Entry<String, List<MessageData>> data : datas.entrySet()) {
			mt = new MessageThread(UUIDHelper.uuidToString(UUID.randomUUID()), data.getKey(), data.getValue());
			for (MessageData msgDt : data.getValue())
				mt.getParticipants().add(msgDt.getSender());
			
			userProfile.addThreadMessage(mt);
			threads.add(mt);
		}
		return threads;
	}

	@POST
	@Produces(MediaType.TEXT_PLAIN)
	@Path("/friends/invite/{emails}")
	public Response inviteFriends(@PathParam("friendId") String emails) {
		try {
			InvitationData iData = new InvitationData(UUID.randomUUID(), userProfile.getName(), emails, new Date());
			invitationService.addInvitation(iData);
			//TODO check whether the link for registration page is correct
			MailMe mail = new MailMe("Invitation to Babytivities", "noreply@babytivities.com", 
						emails.replace(",", ";").split(";"), 
						"invitation", new String[] {userProfile.getName(),
						BABY_HOME + "/registerpage?ref=" + UUIDHelper.uuidToString(iData.getId())});
			
			mailService.sendMail(mail);
		} catch (Exception e) {
			log.error("error inviting friends: " + emails, e);
			return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
		}
		return Response.status(Response.Status.OK).build();
	}
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/user/baby/{age}")
	public TipsWrapper getTipsWrapper(@PathParam("babyAge") float babyAge) {
		List<TipsData> tipsDatas = tipsService.findTipsData(babyAge);
		return new TipsWrapper(tipsDatas);
	}
	
	public boolean confirmInvitation(String confirmId) {
		try {
			InvitationData iData = invitationService.find(UUIDHelper.stringToUUID(confirmId));
			if (iData != null) {
				connectingUser(iData.getSender(), iData.getRecipient());
				connectingUser(iData.getRecipient(), iData.getSender());
			}
		} catch (Exception e) {
			log.error("error confirm id: " + confirmId, e);
			return false;
		}
		return true;
	}

	private void connectingUser(String sender, String recipient) throws Exception {
		UserData userData = userService.find(sender);
		if (userData != null) {
			UserNetworkData data = new UserNetworkData(UUID.randomUUID(), 
					ModelHelper.convertUserData(userData), recipient, new Date());
			userNetworkService.addNetwork(data);
		}
	}
}
